package com.chushouya.manager.service.admin.impl;


import com.chushouya.common.constant.BusinessCode;
import com.chushouya.common.util.PasswordUtil;
import com.chushouya.manager.service.admin.*;
import com.general.framework.core.lang.Jsons;
import com.chushouya.manager.dao.entity.AdminUserEntity;
import com.chushouya.manager.dto.admin.log.LoginLogDTO;
import com.chushouya.manager.dto.admin.login.AccountLoginDTO;
import com.chushouya.manager.dto.admin.login.LoginUser;
import com.chushouya.manager.dto.admin.user.AdminExtendInfoDTO;
import com.general.framework.core.constant.Constants;
import com.general.framework.core.enums.BusinessStatus;
import com.general.framework.core.enums.ContextUserType;
import com.general.framework.core.enums.StatusEnum;
import com.general.framework.core.exception.Ex;
import com.general.framework.core.lang.Check;
import com.general.framework.core.lang.Dates;
import com.general.framework.core.lang.Strings;
import com.general.framework.data.redis.RedisCache;
import com.general.framework.web.utils.IpAddressUtil;
import com.general.framework.web.utils.IpUtils;
import com.general.framework.web.utils.ServletUtils;
import com.chushouya.manager.service.admin.*;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.Set;

/**
 * 登录业务
 * @author leejean team
 */
@Service
@Slf4j
public class AdminLoginServiceImpl implements AdminLoginService {
    /**
     * 登录是否校验验证码
     * test mode
     */
    @Value("${com.chushouya.setting.validateCaptcha:true}")
    private boolean validateCaptcha;


    @Resource
    private AdminTokenService adminTokenService;

    @Resource
    private RedisCache redisCache;

    @Resource
    private AdminUserService adminUserService;

    @Resource
    private PermissionService permissionService;

    @Resource
    private LoginLogService loginLogService;


    @Override
    public String accountLogin(ContextUserType userType, AccountLoginDTO accountLogin) {
        String userName = accountLogin.getUserName(), password = accountLogin.getPassword(), code = accountLogin.getCode(), uuid = accountLogin.getUuid();
        // 验证码验证
        final String captchaCodeKey = Constants.CAPTCHA_CODE_KEY + uuid;
        this.validateCaptcha(userType, userName, code, captchaCodeKey);
        // 用户验证
//        AdminUserEntity adminUser = adminUserService.selectUserName(userType.value(), userName);
        AdminUserEntity adminUser = adminUserService.selectUserName(null, userName);
        try {
//            Check.notNull(adminUser, "{}不存在", userType.named());
            Check.notNull(adminUser, "用户不存在");
            boolean isMatches = PasswordUtil.matchesPassword(password, adminUser.getPassword());
            if (!isMatches) {
                String msg = BusinessCode.username_or_password_mismatch.msg();
                this.saveLoginLog(userType, userName, Constants.LOGIN_FAIL, msg);
                throw Ex.business(BusinessCode.username_or_password_mismatch.code(), msg);
            }
            this.validateAdminUser(adminUser);
        } catch (Exception e) {
            this.saveLoginLog(userType, userName, Constants.LOGIN_FAIL, e.getMessage());
            throw Ex.business(e.getMessage());
        }
        LoginUser loginUser = this.buildLoginUser(adminUser);
        this.setLastLoginInfo(adminUser.getAdminUserId());
        this.saveLoginLog(userType, userName, Constants.LOGIN_SUCCESS, "登录成功");
        redisCache.deleteObject(captchaCodeKey);
        return adminTokenService.createToken(loginUser);
    }

    @Override
    public LoginUser getLoginUser(String token) {
        return adminTokenService.getLoginUser(token);
    }

    private void saveLoginLog(ContextUserType userType, String userName, String status, String message) {
        final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        LoginLogDTO loginLog = new LoginLogDTO();
        loginLog.setUserType(userType.value());
        loginLog.setUserName(userName);

        final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        loginLog.setLoginIp(ip);
        loginLog.setLoginLocation(IpAddressUtil.getAddress(ip));

        String os = userAgent.getOperatingSystem().getName();
        String browser = userAgent.getBrowser().getName();
        loginLog.setLoginDetail(Strings.format("os:{},browser:{}", os, browser));
        if (Strings.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
            loginLog.setStatus(BusinessStatus.SUCCESS.getCode());
        } else if (Constants.LOGIN_FAIL.equals(status)) {
            loginLog.setStatus(BusinessStatus.FAIL.getCode());
        }
        loginLog.setMsg(message);
        loginLog.setLoginTime(Dates.getTimeNow());

        try {
            loginLogService.saveLoginLog(loginLog);
        } catch (Exception e) {
            log.error("调用RPC保存登录日志失败:{}", e.getMessage());
        }
    }

    private LoginUser buildLoginUser(AdminUserEntity adminUser) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserType(adminUser.getUserType());
        loginUser.setUserId(adminUser.getAdminUserId());
        loginUser.setUserName(Strings.format("{}({})", adminUser.getNickName(), adminUser.getUserName()));
        Set<String> permissions = permissionService.getAdminPermission(loginUser.getUserId());
        Set<String> roles = permissionService.getRoles(adminUser.getAdminUserId());
        loginUser.setPermissions(permissions);
        loginUser.setRoles(roles);
        loginUser.setExtraInfo(Jsons.parseObject(adminUser.getExtendInfo(), AdminExtendInfoDTO.class));
        return loginUser;
    }

    @Override
    public void validateAdminUser(AdminUserEntity adminUser) {
        String userName = adminUser.getUserName();
        if (Objects.equals(StatusEnum.disable.value(), adminUser.getStatus())) {
            throw Ex.business(BusinessCode.user_is_disable.code(), BusinessCode.user_is_disable.msg(), userName);
        }
    }

    /**
     * 校验验证码
     */
    private void validateCaptcha(ContextUserType userType, String userName, String code, String captchaCodeKey) {
        if (this.validateCaptcha) {
            String captcha = redisCache.getCacheObject(captchaCodeKey);
            if (captcha == null) {
                this.saveLoginLog(userType, userName, Constants.LOGIN_FAIL, BusinessCode.validate_captcha_invalid.msg());
                throw Ex.business(BusinessCode.validate_captcha_invalid.code(), BusinessCode.validate_captcha_invalid.msg());
            }
            if (!code.equalsIgnoreCase(captcha)) {
                this.saveLoginLog(userType, userName, Constants.LOGIN_FAIL, BusinessCode.validate_captcha_mismatch.msg());
                throw Ex.business(BusinessCode.validate_captcha_mismatch.code(), BusinessCode.validate_captcha_mismatch.msg());
            }
            redisCache.deleteObject(captchaCodeKey);
        } else {
            log.info("Tip: The config validateCaptcha is false, skip validate code");
        }
    }

    /**
     * 记录登录信息
     */
    private void setLastLoginInfo(Long userId) {
        adminUserService.updateLastLoginInfo(userId, IpUtils.getIpAddr(ServletUtils.getRequest()));
    }

    @Override
    public void logout(String token) {
        adminTokenService.delLoginToken(token);
    }

}
